// License Notification
//======================================================
// Copyright (c) 2010 Channel Intelligence Inc.
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// which accompanies this distribution, and is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// ======================================================




using System;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Xml.Linq;
using System.Text;
using System.Reflection;
using System.Windows.Forms;
using CIP4.Common;
using CIP4.Simple;

namespace CIP4.Browser
{
    public partial class MainBrowserForm : Form
    {
        public static MainBrowserForm TheForm;

        private ViewType _CurrentView;
        public ViewType CurrentView
        {
            get
            {
                if (_CurrentView == null)
                    _CurrentView = new ViewType(Logger);
                return _CurrentView;
            }
            set
            {
                _CurrentView = value;
            }
        }

        private bool TemplateChanged { get; set; }

        private FileInfo _CurrentAssemblyContext;
        private FileInfo CurrentAssemblyContext 
        {
            get
            {
                return _CurrentAssemblyContext;
            }
            set
            {
                if (_CurrentAssemblyContext != null)
                    Globals.AddAssemblyPath(_CurrentAssemblyContext.DirectoryName, Logger);
                if (value != null)
                    Globals.AddAssemblyPath(value.DirectoryName, Logger);
                _CurrentAssemblyContext = value;
            }
        }

        private List<Assembly> _Assemblies;
        public List<Assembly> Assemblies
        {
            get
            {
                if (_Assemblies == null)
                {
                    _Assemblies = new List<Assembly>();
                }
                return _Assemblies;
            }
        }

        public MainBrowserForm()
        {
            TheForm = this;
            InitializeComponent();
            toolStrip2.Visible = true;

            ViewType selectedView = null;
            string askingForThisView = string.Empty;

            if (Environment.GetCommandLineArgs().Length > 1)
                askingForThisView = Environment.GetCommandLineArgs()[1];

            foreach (ViewType view in ViewType.Wrapper.GetItems(null, Logger))
            {
                view.Clean();
                if (view.AssemblyPath == askingForThisView)
                    selectedView = view;
                view.SetLogger(Logger);
                CurrentViewsComboBox.Items.Add(view);
            }

            if (selectedView == null && File.Exists(askingForThisView))
                selectedView = NewView(askingForThisView);

            if (CurrentViewsComboBox.Items.Count > 0 && selectedView == null)
                selectedView = (ViewType)CurrentViewsComboBox.Items[0];
            if (selectedView != null)
                LoadView(selectedView);
        }

        public LoggerResponseEnum Logger(MajorEnum status, MinorEnum minorStatus, string message, object[] paras)
        {
            string statusMessage = string.Format("{0}{1} {2}", status, minorStatus == MinorEnum.Generic ? string.Empty : "." + minorStatus.ToString(), string.Format(message, paras));
            TheStatusLabel.Text = statusMessage.Length > 80 ? (statusMessage.Substring(0, 80) + "...see log tab for details") : statusMessage; 
            Program.WriteLog(status, string.Format("{0} {1}", minorStatus == MinorEnum.Generic ? string.Empty : "." + minorStatus.ToString(), string.Format(message, paras)));
            LogListBox.Items.Insert(0,
                new ListBoxLogEntryType
                {
                    Status = status,
                    MinorStatus = minorStatus,
                    Message = message,
                    Paras = paras,
                    DeliveryTime = DateTime.Now
                });
            if (LogListBox.Items.Count > 1000)
                LogListBox.Items.RemoveAt(LogListBox.Items.Count - 1);
            return LoggerResponseEnum.keepGoing;
        }

        private void OpenAssemblyMenuItem_Click(object sender, EventArgs e)
        {
            if (OpenAssemblyFileDialog.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    if (CurrentView == null)
                    {
                        LoadView(NewView(OpenAssemblyFileDialog.FileName));
                    }
                    else
                    {
                        ViewType newView = NewView(OpenAssemblyFileDialog.FileName);
                        if (newView != null)
                        {
                            //CurrentView.AssemblyPath = newView.AssemblyPath;
                            LoadView(newView);
                        }
                    }
                }
                catch (Exception ex)
                {
                    Logger(MajorEnum.Error, MinorEnum.Generic, "Error opening assembly {0}: {1}", new object[] { OpenAssemblyFileDialog.FileName, ex });
                }
            }
        }

        private void NewViewButton_Click(object sender, EventArgs e)
        {
            if (OpenAssemblyFileDialog.ShowDialog() == DialogResult.OK)
            {
                LoadView(NewView(OpenAssemblyFileDialog.FileName));
            }
        }

        private void OpenTypeMenuItem_Click(object sender, EventArgs e)
        {
            if (CurrentView != null && CurrentView.GetViewAssembly() != null)
            {
                ChooseTypeForm ctf = new ChooseTypeForm(CurrentView.GetViewAssembly(), Logger);
                if (ctf.ShowDialog() == DialogResult.OK)
                {
                    CurrentView.SetViewType(ctf.SelectedType);
                    if (CurrentView.Connection == null)
                        EDitConnectionButton_Click(this, new EventArgs());
                    LoadView(CurrentView);
                }
            }
        }

        private ViewType NewView(string assemblyPath)
        {
            try
            {
                CurrentAssemblyContext = new FileInfo(assemblyPath);
                if (!CurrentAssemblyContext.Exists)
                {
                    Logger(MajorEnum.Error, MinorEnum.Generic, "The given assmbly does not exist: {0}", new object[] { assemblyPath });
                    return null;
                }
                ViewType newView = new ViewType(Logger);
                newView.AssemblyPath = assemblyPath;
                ChooseTypeForm ctf = new ChooseTypeForm(newView.GetViewAssembly(), Logger);
                if (ctf.ShowDialog() == DialogResult.OK)
                {
                    newView.SetViewType(ctf.SelectedType);
                    if (newView.Connection == null)
                        EditConnection(newView);
                    return newView;
                }
                return null;
            }
            catch (Exception ex)
            {
                Logger(MajorEnum.Error, MinorEnum.Generic, "There was an error loading the assembly {0}: {1}", new object[] { assemblyPath, ex} );
                return null;
            }
            finally
            {
                CurrentAssemblyContext = null;
            }
        }

        bool LoadingAView = false;

        private void LoadView(ViewType view)
        {
            if (LoadingAView)
                return;
            if (view == null)
            {
                TheStatusLabel.Text = "Not OK: nothing loaded";
                return;
            }
            if (view.GetViewAssembly() == null)
            {
                TheStatusLabel.Text = "Not OK: The view assembly cannot be loaded so the view is unusable";
                return;
            }
            try
            {
                CurrentAssemblyContext = new FileInfo(view.AssemblyPath);
                if (view.GetViewType() == null)
                {
                    TheStatusLabel.Text = "Not OK: The view Type cannot be loaded so the view is unusable";
                    return;
                }
                if (view.Connection == null)
                {
                    EditConnectionForm ecf = new EditConnectionForm(view);
                    ecf.Text = string.Format("CIP4 Browser: Connection for {0} in {1}", view.CurrentType, Path.GetFileName(view.AssemblyPath));
                    if (ecf.ShowDialog() != DialogResult.OK)
                    {
                        TheStatusLabel.Text = "Not OK: There is no connection for the view so the view is unusable";
                        return;
                    }
                }

                try
                {
                    LoadingAView = true;
                    CurrentView = view;
                    LoadTemplate(false);
                    LoadItems();
                    ViewPropertyGrid.SelectedObject = CurrentView;
                    CurrentViewsComboBox.Items.Remove(CurrentView);
                    CurrentViewsComboBox.Items.Insert(0, CurrentView);
                    CurrentViewsComboBox.SelectedIndex = 0;
                }
                finally
                {
                    LoadingAView = false;
                }
            }
            catch (Exception ex)
            {
                Logger(MajorEnum.Error, MinorEnum.Generic, "Error opening assembly {0}: {1}", new object[] { OpenAssemblyFileDialog.FileName, ex });
            }
            finally
            {
                CurrentAssemblyContext = null;
            }
        }

        private void LoadTemplate(bool loadNew)
        {
            //if (CurrentView.CurrentTemplate != null)
            //{
            //    TemplateItemPropertyGrid.SelectedObject =
            //        CurrentView.GetObjectForXml(CurrentView.CurrentTemplate.XmlForItem());
            //    if (TemplateItemPropertyGrid.SelectedObject != null)
            //        return;
            //}
            Type templateType = CurrentView.GetViewType();
            TemplateChanged = false;
            if (CurrentView.CurrentTemplate != null && !loadNew)
            {
                TemplateItemPropertyGrid.SelectedObject = CurrentView.CurrentTemplate;
            }
            else if (templateType != null)
            {
                ConstructorInfo cons = templateType.GetConstructor(new Type[] { });
                object templateItem = cons.Invoke(new object[] { });
                TemplateItemPropertyGrid.SelectedObject = templateItem;
            }
            else
                TemplateItemPropertyGrid.SelectedObject = null;
        }

        IEnumerator<object> CurrentItems;

        private void LoadItems()
        {
            ItemListBox.Items.Clear();
            ItemListBox.DisplayMember = CurrentView.ExternalIDPropertyName;
            CurrentItems = CurrentView.GetItems().GetEnumerator();
            NextButton_Click(this, new EventArgs());
        }

        private void NextButton_Click(object sender, EventArgs e)
        {
            int itemCount = 0;
            while (CurrentItems.MoveNext())
            {
                ItemListBox.Items.Add(CurrentItems.Current);
                itemCount++;
                if (itemCount >= Math.Max(CurrentView.BatchSize, 1))
                    break;
            }
            Logger(MajorEnum.Information, MinorEnum.Generic, "{0} items loaded, {1} items altogether", new object[] { itemCount, ItemListBox.Items.Count });
        }

        private void ItemListBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (ItemListBox.SelectedItem == null)
                return;
            ItemPropertyGrid.SelectedObject = ItemListBox.SelectedItem;
            ItemXmlTextBox.Text = XElement.Parse(CurrentView.GetXmlForObject(ItemListBox.SelectedItem)).ToString();
        }

        private void LogListBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            LogDetailsTextBox.Text = string.Empty;
            if (LogListBox.SelectedItem == null)
                return;
            ListBoxLogEntryType lbe = (ListBoxLogEntryType)LogListBox.SelectedItem;
            LogDetailsTextBox.Text = lbe.ToLongString();
        }

        private void ClearTemplateButton_Click(object sender, EventArgs e)
        {
            LoadTemplate(true);
        }

        private void RefreshButton_Click(object sender, EventArgs e)
        {
            LoadItems();
        }

        private void TemplateItemPropertyGrid_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
        {
            TemplateChanged = true;
        }

        private void SaveViewButton_Click(object sender, EventArgs e)
        {
            List<ViewType> views = new List<ViewType>();
            foreach (ViewType view in ViewType.Wrapper.GetItems(null, Logger))
                views.Add(view);
            ViewType.Wrapper.PurgeItems(ViewExternalIDs(views), Logger);
            views.Clear();
            foreach (ViewType view in CurrentViewsComboBox.Items)
                views.Add(view);
            ViewType.Wrapper.SaveItems(views, Logger);
            foreach (ViewType view in views)
                view.Clean();
        }

        private IEnumerable<ExternalIDType> ViewExternalIDs(IEnumerable<ViewType> views)
        {
            foreach (ViewType view in views)
                yield return ExternalIDType.ExternalIDFromEscapedString( view.ExternalID );
        }

        private void SaveItemsButton_Click(object sender, EventArgs e)
        {
            List<object> items = new List<object>();
            foreach (object item in ItemListBox.SelectedItems)
                items.Add(item);

            CurrentView.SaveItems(items);
        }

        private void SaveAsItemsButton_Click(object sender, EventArgs e)
        {
            // ask for a connection - save the currently selected items in the given collection
            MessageBox.Show("Not implemented yet");

        }

        private void DeleteItemButton_Click(object sender, EventArgs e)
        {
            List<object> items = new List<object>();
            foreach (object item in ItemListBox.SelectedItems)
                items.Add(item);

            CurrentView.PurgeItems(items);

            foreach (object item in items)
                ItemListBox.Items.Remove(item);
        }

        private void EDitConnectionButton_Click(object sender, EventArgs e)
        {
            EditConnection(CurrentView);
            ViewPropertyGrid.SelectedObject = CurrentView;
        }

        private void EditConnection(ViewType forView)
        {
            EditConnectionForm ecf = new EditConnectionForm(forView);
            ecf.ShowDialog();
        }

        private void CurrentViewsComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (LoadingAView)
                return;
            if (CurrentViewsComboBox.SelectedItem == null)
                return;
            LoadView((ViewType)CurrentViewsComboBox.SelectedItem);
        }

        private void MainBrowserForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            foreach (ViewType view in CurrentViewsComboBox.Items)
            {
                if (view.IsDirty)
                {
                    if (MessageBox.Show("You have unsaved views - do you want to save them?", "CIP4 Browser", MessageBoxButtons.YesNo) == DialogResult.Yes)
                    {
                        SaveViewButton_Click(this, new EventArgs());
                        break;
                    }
                }
            }
            foreach (ViewType view in CurrentViewsComboBox.Items)
            {
                view.ResetStrongWrapper();
            }
            ViewType.Wrapper.Dispose();
        }

        private void ViewPropertyGrid_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
        {
            CurrentView.ResetStrongWrapper();
            Logger(MajorEnum.Information, MinorEnum.Generic, "CurrentView is now {0}", new object[] { CurrentView.ToString() });
        }
    }
}
